var urls = {
    LOGIN_C_URL: "https://html.m.cmbchina.com/MobileHtml/Login/LoginC.aspx",
    CAPTHCA_URL: "https://html.m.cmbchina.com/MobileHtml/Login/ExtraPwd.aspx?ClientNo=%s&random=%s",
    SEND_SMS_URL: "https://html.m.cmbchina.com/MobileHtml/Login/MsgVerify.aspx",
    SUBMIT_SMS_URL: "https://html.m.cmbchina.com/MobileHtml/Login/MsgVerify.aspx",
    ACOUNT_URL: "https://html.m.cmbchina.com/MobileHtml/creditcard/m_accountmanage/am_queryaccount.aspx",
    BILL_LIST_URL: "https://html.m.cmbchina.com/MobileHtml/creditcard/m_accountmanage/am_queryreckoningsurvey.aspx",
    BILL_URL: "https://html.m.cmbchina.com/MobileHtml/CreditCard/M_AccountManage/am_QueryReckoningList.aspx",
    WEI_OUT_URL: "https://html.m.cmbchina.com/MobileHtml/creditcard/m_accountmanage/am_queryreckoningsurveynot.aspx",
    WEI_OUT_DETAIL_URL: "https://html.m.cmbchina.com/MobileHtml/CreditCard/M_AccountManage/am_QueryReckoningSurveyNot.aspx"
}
var params = {
    USER_AGENT: "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
    ACCEPT: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    //身份证参数
    CERT_XML_REQ: "<LoginMode>0</LoginMode>"
    + "<LoginByCook>false</LoginByCook>"
    + "<IDTypeC>01</IDTypeC>"
    + "<IDNoC>%s</IDNoC>"
    + "<PwdC>%s</PwdC>"
    + "<ExtraPwdC>%s</ExtraPwdC>"
    + "<RememberFlag>false</RememberFlag>"                                                                                 //Chrome/58.0.3029.110 Mobile Safari/537.36
    + "<UserAgent>Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36</UserAgent>"
    + "<W>444</W>"//400
    + "<H>252</H>"//409
    + "<OS>Win32</OS>",
    //卡号参数
    CREDITCARDNO: "<CreditCardNo>%s</CreditCardNo>"
    + "<PwdC>%s</PwdC>"
    + "<ExtraPwdC>%s</ExtraPwdC>"
    + "<LoginMode>1</LoginMode>"
    + "<LoginByCook>false</LoginByCook>"
    + "<RememberFlag>false</RememberFlag>"
    + "<UserAgent>Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Mobile Safari/537.36</UserAgent>"
    + "<W>400</W>"
    + "<H>550</H>"
    + "<OS>Win32</OS>"


}


// 如果页面含有“退出”，说明登录成功
var SUCCESS_TAG = "退出";
// 如果页面含有“短信验证”，说明需要短信验证码
var SMS_TAG = "短信验证";
// 提交请求的时候需要用，
var clientNo = "";
// 是否需要短信验证码
var needSmsFlag = false;
// 账单日（不包含年月）
var billDate = "";
// 还款日（不包含年月）
var repaymentDate = "";
// 完整卡号
var cardNo = "";
//账单月
var billMonth = "";
//还款月
var payMonth = "";
var contextMap = new HashMap();
var remark_regx = /[\s\S]*\d+-\d+/
var needReLogin = true;
var lastInstallList = null;


var v6_cmb_mobile_credit_java_funcs = {
    openHomePage : function(){
        logger.info("任务【{}】,打开首页",context.get(LUA_TASK_KEY));
        var indexHtml = cmbSubmitWithReTry(urls.LOGIN_C_URL,null,null,"获取登录页面","get");
        storeDataToFile(context, indexHtml, "loginPage.html")
        var doc = Jsoup.parse(indexHtml)
        var captchaUrl = doc.select("input[type='image']").attr("src")
        context.put("captchaUrl", captchaUrl)
        if(StringUtils.isNotBlank(captchaUrl) && captchaUrl.split("\?").length>=2 && captchaUrl.split("\?")[1].split("&").length>=1 && captchaUrl.split("\?")[1].split("&")[0].split("=").length>=2){
            clientNo = captchaUrl.split("\?")[1].split("&")[0].split("=")[1]
        }
    },
    checkProxyAvailable: function () {
        var captchaUrl = context.get("captchaUrl")
        return StringUtils.isNotBlank(captchaUrl)
    },
    checkTargetWebsiteAvailable: function(){
        //将成功获取到ClientNo定义为目标网站可用
        return StringUtils.isNotBlank(clientNo)
    },
    needResetEnvironment : function() {
        return equals(context.get("currentErrorCode"),BALO_99602_50) || equals(context.get("currentErrorCode"),BALO_22006_10);
    },
    checkControlAvailable: function () { return true; },
    login:function () {
        if(needReLogin){
            logger.info("任务【{}】,招商银行信用卡开始登录",context.get(LUA_TASK_KEY));
            //获取图片验证码和登录参数
            logger.info("任务【{}】,招商银行开始进行登录参数获取，并进行登录参数拼装",context.get(LUA_TASK_KEY));
            var xmlReq = null;
            var loginNoTmp = getLoginParams(context)[0];
            if (equals(IDCARD, LoginType.stringToLoginType(context.get(LUA_TASK_LOGIN_TYPE)))) {//身份证
                xmlReq = params.CERT_XML_REQ;
            } else if (equals(CREDITCARDNO, LoginType.stringToLoginType(context.get(LUA_TASK_LOGIN_TYPE)))) {//卡号
                xmlReq = params.CREDITCARDNO;
            }
            var password = context.get(LUA_TASK_TOKEN);
            if (password.length() > 6) {
                logger.info("{},密码的前六位:{}", context.get(LUA_TASK_KEY), password.substring(0, 6));
            }
            logger.info("任务【{}】，开始获取图片验证码",context.get(LUA_TASK_KEY));
            var random = new Random();
            context.put(Constants.TASK_IMG_BYTES, RequestUtil.getAsBytes(format(urls.CAPTHCA_URL, clientNo, random.nextFloat())));
            context.put(TASK_IMG_REGEX,"[0-9]{4}");
            var captchaStr = waitImgCodeBySecondsByJs(context,60,true,3);
            logger.info("任务【{}】,获取到的图片验证码为：【{}】",context.get(LUA_TASK_KEY),captchaStr);
            xmlReq = String.format(xmlReq, loginNoTmp, password, captchaStr);
            context.put("xmlReq",xmlReq);
            logger.info("任务【{}】,拼装后的登录参数为：【{}】",context.get(LUA_TASK_KEY),xmlReq);

            //开始发送登录请求
            var loginHtml = getLoginHtml();
            logger.info("任务【{}】，登录结果：【{}】",context.get(LUA_TASK_KEY),loginHtml)
            //校验登录的流程是否正确，设置是否需要短信验证码的标志
            var isLoginCorrectFunc = function(loginHtml){
                if(StringUtils.contains(loginHtml, SUCCESS_TAG)) {
                    context.put("needSms", false)
                    return true
                } else if(StringUtils.contains(loginHtml, SMS_TAG)){
                    context.put("needSms", true)
                    return true
                } else {
                    return false
                }

            }
            //获取错误信息
            var getLoginErrorMsg = function(loginHtml){
                if(loginHtml.contains("为了您的帐户安全，设置密码时请不要选用您的身份证、生日、手机号码、卡号、重复或连续等简单的数字。")){
                    return "为了您的帐户安全，设置密码时请不要选用您的身份证、生日、手机号码、卡号、重复或连续等简单的数字。"
                } else if(loginHtml.contains("无效附加码#2")){
                    return "图片验证码错误"
                } else if(loginHtml.contains("亲爱的准信用卡客户，请您登录掌上生活客户端进行相关业务的查询与办理")){
                    return "亲爱的准信用卡客户，请您登录掌上生活客户端进行相关业务的查询与办理";
                }else if(loginHtml.contains("信用卡开卡")){
                    return "信用卡开卡"
                }else if(loginHtml.contains("由于您的请求正在处理中，请不要频繁操作，稍候片刻再返回菜单继续使用。")){
                    return "频繁操作"
                }else if(getGroupByIndex("errMsg = \"(.*)\";", loginHtml, 1).contains("不能在当前渠道使用，如需使用，请登录“网上银行专业版”在“专业版管理”中重新开通使用。")){
                    return "不能在当前渠道使用，如需使用，请登录“网上银行专业版”在“专业版管理”中重新开通使用。"
                } else if(StringUtils.contains(loginHtml, "Service Unavailable")){
                    //Service Unavailable
                    return "系统繁忙，请稍后再试。"
                } else {
                    return getGroupByIndex("errMsg = \"(.*)\";", loginHtml, 1)
                }

            }
            assertTrueOrThrowException(loginHtml, isLoginCorrectFunc, getLoginErrorMsg)

            //短信验证码操作
            if(context.get("needSms")){
                logger.info("任务【{}】，需要短信验证功码，开始发送",context.get(LUA_TASK_KEY));
                //发送短信验证码
                var parametersMap = new HashMap();
                parametersMap.put("ClientNo", clientNo);
                parametersMap.put("MsgVerifyCmd", "Ajax_MSG_SEND");
                parametersMap.put("MsgFlag_", "010000");
                parametersMap.put("ddlMobile", "1");
                parametersMap.put("$RequestMode$", "1");
                parametersMap.put("MsgCode", "");
                parametersMap.put("Command", "");
                parametersMap.put("MsgTips_", "请选择一个手机号码，并点击'发送短信验证码'按钮来发送验证码。如果你已经取得验证码，请直接在下面的文本框中输入即可。");
                var smsHeader = new HashMap();
                smsHeader.put("User-Agent", params.USER_AGENT);
                smsHeader.put("Accept", "*/*");
                smsHeader.put("Host", "html.m.cmbchina.com");
                smsHeader.put("Origin", "https://html.m.cmbchina.com");
                smsHeader.put("Referer", "https://html.m.cmbchina.com/MobileHtml/Login/LoginC.aspx");
                smsHeader.put("X-Requested-With", "XMLHttpRequest");
                smsHeader.put("Accept-Encoding", "gzip, deflate, br");
                smsHeader.put("Connection", "keep-alive");
                smsHeader.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
                var html = post(urls.SEND_SMS_URL, parametersMap, smsHeader);
                storeDataToFile(context, html, "smsSendResult.json")
                logger.info("任务【{}】，短信验证码发送结果【{}】",context.get(LUA_TASK_KEY),html);

                //check开始: 检查短信是否发送成功或是否是短信过于频繁
                var isSendSmsCorrectFunc = function(html) {
                    var content = JSON.parse(html).get("$SysResult$").get("$Content$")
                    var msg = JSON.parse(content)
                    return msg !== null
                        && msg.get("DispMsg").contains("短信验证码已发送至手机")
                        && msg.get("DispMsg").contains("请在5分钟内完成验证")
                }

                var getSendSmsErrorMsgFunc = function(html) {
                    var content = JSON.parse(html).get("$SysResult$").get("$Content$")
                    var sysCode = JSON.parse(html).get("$SysResult$").get("$SysCode$")
                    var msg = JSON.parse(content)
                    if(msg !== null){
                        if(msg.get("DispMsg").contains("您在24小时内验证码申请次数超过20次")){
                            return "您在24小时内验证码申请次数超过20次,请明天再试。";
                        }else if("406".equals(sysCode)){
                            return "频繁操作"
                        }else{
                            return msg.get("DispMsg")
                        }
                    } else {
                        return ""
                    }
                }
                assertTrueOrThrowException(html, isSendSmsCorrectFunc, getSendSmsErrorMsgFunc)
            }
        }

        if(context.get("needSms")){
            // 获取短信验证码
            var verifyMessage = waitSmsCode();
            logger.info("任务【{}】，用户输入的短信验证码【{}】",context.get(LUA_TASK_KEY),verifyMessage);
            //check开始: 检查短信是否发送成功或是否是短信过于频繁
            var isSmsCodeCorrectFunc = function(verifyMessage) {
                return StringUtils.isNotBlank(verifyMessage) && verifyMessage.length === 6
            }
            var getSmsCodeErrorMsgFunc = function(verifyMessage) {
                return "短信验证码错误"
            }
            assertTrueOrThrowException(verifyMessage, isSmsCodeCorrectFunc, getSmsCodeErrorMsgFunc)

            logger.info("任务【{}】，开始进行最终的登录提交",context.get(LUA_TASK_KEY));
            parametersMap = new HashMap();
            parametersMap.put("ClientNo", clientNo);
            parametersMap.put("MsgFlag_", "110000");
            parametersMap.put("MsgCode", verifyMessage);
            parametersMap.put("ddlMobile", "1");
            parametersMap.put("Command", "CMD_VERIFY");
            parametersMap.put("DeviceTAB", "TabDefault");

            var receiveSmsHeader = new HashMap();
            receiveSmsHeader.put("User-Agent", params.USER_AGENT);
            receiveSmsHeader.put("Accept", params.ACCEPT);
            receiveSmsHeader.put("Host", "html.m.cmbchina.com");
            receiveSmsHeader.put("Origin", "https://html.m.cmbchina.com");
            receiveSmsHeader.put("Referer", "https://html.m.cmbchina.com/MobileHtml/Login/LoginC.aspx");

            //html = post(urls.SUBMIT_SMS_URL, parametersMap, receiveSmsHeader);
            html = cmbSubmitWithReTry(urls.SUBMIT_SMS_URL,parametersMap,receiveSmsHeader,"输入短信验证码","post");
            storeDataToFile(context, html, "loginSuccess.html")
            //html = "验证验证码失败，原因：请重新申请验证码。"
            //判断是否登录成功
            var isSendSmsCorrectFunc = function(html){
                return StringUtils.contains(html, SUCCESS_TAG)
            }
            var getSendSmsMsgFunc = function(html) {
                if(html.contains("请输入正确的验证码或重新申请验证码") || html.contains("验证验证码失败，原因：验证码错误，请重新输入")){
                    needReLogin = false;
                }else if(html.contains("验证码已过期，请重新申请验证码") || html.contains("验证验证码失败，原因：请重新申请验证码") || html.contains("验证错误次数已超过3次，请重新申请验证码")){
                    v4_cmb_mobile_credit_java_funcs.openHomePage();//重新打开下首页
                    needReLogin = true;
                }
                if(html.contains("由于您的请求正在处理中，请不要频繁操作，稍候片刻再返回菜单继续使用。") || html.contains("请重新登录") || html.contains("操作失败!")){
                    return "频繁操作"
                }
                return Jsoup.parse(html).select("#TitleMsg").text()
                //return "验证验证码失败，原因：请重新申请验证码。"
            }
            assertTrueOrThrowException(html, isSendSmsCorrectFunc, getSendSmsMsgFunc);
        }
    },
    crawl: function () {
        var cardNum = 1;
        //向上下文中设置信用卡数量
        context.put(keys.CRED_CARD_SIZE,1);
        //构建表头
        createData();
        //获取构建到的表头信息
        var data_list = context.get(ALL_DATA);
        //因为招商的卡都是统一的账单，所以只有一个数据对象
        var creditCardAllData = data_list.get(0);

        // #########################################抓取卡片 -- 解析卡片信息#############################################
        crawlCardInfo(cardNum);
        parseCardInfo(creditCardAllData);

        // ##########################################抓取账户 -- 解析账户信息###########################################
        crawlAcountInfo(cardNum);
        parseAccountInfo(creditCardAllData);

        // #########################################开始进行已出账单的处理################################################
        // 抓取已出账单外层列表
        crawlBillsInfo(cardNum);
        //解析卡号信息
        var creditAccNo = getCreditAccNo();
        //解析已出账单信息
        //12个月
        var sixMon = getBankTimeList(billDate);
        for (var i = 0; i < sixMon.size(); i++) {
            //获得每个月
            var oneMonth = sixMon.get(i);
            var yearStr = Integer.parseInt(oneMonth.substring(0, 4));
            var monStr = Integer.parseInt(oneMonth.substring(5, 7));
            logger.info("12个月中的每一个月:{},{}", yearStr, monStr);
            var installments = new ArrayList();
            var shopping_sheets = new ArrayList();
            //初始化bill对象
            var bill_month = oneMonth;
            //现获取到一个可用的已出账单的billAllData
            var billAllData = getDoneBillAllDataHeaderData(creditCardAllData);
            print(billAllData);

            oneMonth = oneMonth.replaceAll("-", "");
            // 抓取已出账单汇总信息
            getDetailBillTotalMsg(creditAccNo,oneMonth,monStr,cardNum);
            // 解析已出账单汇总信息
            parseDetailBillTotalMsg(billAllData,i);
            // 抓取已出账单首页
            getDetailBillByDate(creditAccNo,oneMonth,monStr,cardNum)
            // 解析已出账单首页
            var j = 0;
            var pageSum = parseBillHtmlFirstPage(yearStr,billAllData,shopping_sheets,installments,monStr,j);
            // 判断是否有下一页
            var pages = null;
            // 判断下一页
            j = context.get("cmb_count_j");
            if (pageSum !== "" && typeof(pageSum) != "undefined") {
                pages = pageSum.split("/");
                for (var currentPage = 2; currentPage <= Integer.parseInt(pages[1]); currentPage++) {
                    getBillHtmlNextHtml(creditAccNo,oneMonth,monStr,cardNum,currentPage);
                    parseBillHtmlNextPage(yearStr,monStr,billAllData,shopping_sheets,installments,currentPage,j);
                }
            }
            //处理最终结果
            handleFinalBillResult(bill_month,creditCardAllData,yearStr,billAllData,monStr);
        }


        // #########################################开始进行未出账单的处理################################################
        //获取一个干净的未出账单billAllData
        var billAllDataWithOut = getUNDoneBillAllDataHeaderData(creditCardAllData);
        initBillAllDataWithOut(billAllDataWithOut,creditCardAllData);
        //抓取未出账单首页信息
        getBillHtmlWithOutFirstPage(cardNum);
        var shopping_sheets = new ArrayList();
        var installments = new ArrayList();
        //解析未出账单首页信息
        parseBillHtmlWithOutFirstPage();
        var cardNo = "";
        var pages = context.get("pages");
        var buttons = context.get("buttons");
        var i = 0;
        var installment = null;
        var item = 1;
        var sumNewBalance = 0.0;
        var weiOutNewCharges = 0.0;
        if (buttons!=null && typeof(buttons) != "undefined") {
            for (var j = 0; j < buttons.size(); j++) {
                // 消费
                var expense = new Expense();
                var recID = buttons.get(j).attr("onclick");
                recID = getRecid(recID);
                //获取未出账单详细页面
                getDetailBillHtmlWithOutPage(cardNum,item,recID);
                item = context.get("itemFirst");
                //解析未出账单详细页面
                parseBillDetailHtmlWithOutFirst(expense,cardNo,weiOutNewCharges,sumNewBalance,installment,i,billAllDataWithOut,shopping_sheets);
                i = context.get("theOrderIndexOut");
            }
            //判断下一页
            var page = context.get("page");
            if (page !== "" && typeof(page) != "undefined") {
                for (var currentPage = 2; currentPage <= Integer.parseInt(pages[1]); currentPage++) {
                    //获取未出账单下一页的页面
                    getBillHtmlWithOutNextPage(currentPage,cardNum);
                    //解析未出账单的下一页的页面
                    parseBillHtmlWithOutNextPage(currentPage);
                    var buttonsPage =  context.get("buttonsPage");
                    var item1 = context.get("item1");
                    // for (Element button : buttonsPage) {
                    for (var it = 0; it < buttonsPage.size(); it++) {
                        var expense = new Expense();
                        var recID = buttonsPage.get(it).attr("onclick");
                        var news = getRecid2(recID);
                        //获取未出账单的下一页的详细信息
                        getBillDetailHtmlNextPage(currentPage,news,cardNum,item1);
                        item1 = context.get("itemNext");
                        //解析未出账单的下一页的详细信息
                        parseBillDetailHtmlNextPage(expense,shopping_sheets,sumNewBalance,weiOutNewCharges,installment,billAllDataWithOut,cardNo,i);
                        i = context.get("orderIndexNext");
                    }
                }
            }
        }
        //处理未出账单的最终结果
        //handleLastResultWithOut(creditCardAllData,billAllDataWithOut,installments,shopping_sheets);
        handleLastResultWithOut(billAllDataWithOut,installments,shopping_sheets);
    },
    exitLogin: function () {}
}

/**
 * 处理未出账单的最终结果
 */
function handleLastResultWithOut(billAllDataWithOut,installments,shopping_sheets) {
    try {
        //拿到账单，计算未出账单的上期还款金额，新增金额，本期账单金额
        //计算上期还款金额

        //计算新增金额
        var new_charges = new BigDecimal(0.00);
        for(var i=0;i<shopping_sheets.size();i++){
            var expnese = shopping_sheets.get(i);
            print(expnese.getAmountMoney())
            if(expnese.getAmountMoney()!=null && expnese.getAmountMoney()>0){
                new_charges = new_charges.add(expnese.getAmountMoney())
            }
        }
        billAllDataWithOut.setNewCharges(new_charges);

        //计算本期账单金额 上期的 new_balance + 本期amountMoney
        var amonut_money = new BigDecimal(0.00);
        for(var i=0;i<shopping_sheets.size();i++){
            var expnese = shopping_sheets.get(i);
            if(expnese.getAmountMoney()!=null){
                amonut_money = amonut_money.add(expnese.getAmountMoney())
            }
        }
        var lastBalance = contextMap.get("newBalance");
        if (lastBalance == null) {
            lastBalance = 0;
        }
        logger.info("任务【{}】,lastbalance={}",context.get(LUA_TASK_KEY),lastBalance);
        var new_balance = amonut_money.add(new BigDecimal(lastBalance));
        billAllDataWithOut.setNewBalance(new_balance);

        var last_payment = new BigDecimal(0.00);
        for(var i=0;i<shopping_sheets.size();i++){
            var expnese = shopping_sheets.get(i);
            if(expnese.getAmountMoney()!=null && expnese.getAmountMoney()<0){
                last_payment = last_payment.add(expnese.getAmountMoney())
            }
        }
        billAllDataWithOut.setLastPayment(last_payment);
        billAllDataWithOut.setShoppingSheets(shopping_sheets);
        billAllDataWithOut.setInstallments(installments);
    } catch (e) {
        logger.error("taskKey={} 计算未出账单结果异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    }
}

function initBillAllDataWithOut(billAllData,creditCardAllData) {
    billAllData.setCreditLimit(creditCardAllData.getCreditLimit());
    billAllData.setCashLimit(creditCardAllData.getCashLimit());
    billAllData.setUsdNewBalance(null);
    billAllData.setUsdMinPayment(null);
    billAllData.setUsdLastBalance(null);
    billAllData.setUsdLastPayment(null);
    billAllData.setUsdNewCharges(null);
    billAllData.setUsdInterest(null);
    billAllData.setUsdCreditLimit(null);
    billAllData.setUsdCashLimit(null);
    billAllData.setMinPayment(null);
    billAllData.setInterest(null);
    billAllData.setLastPayment(null);
    var lastBalance = contextMap.get("newBalance");
    logger.info("{},上期账单金额:{}", context.get(LUA_TASK_KEY), lastBalance);
    billAllData.setLastBalance(null !== lastBalance ? new BigDecimal(lastBalance) : null);
    var calendar = Calendar.getInstance();
    var year = calendar.get(Calendar.YEAR);
    year = Integer.parseInt(year);
    var month = calendar.get(Calendar.MONTH) + 1;// 获取当前月份
    month = Integer.parseInt(month);
    var datenum = calendar.get(Calendar.DAY_OF_MONTH);// 获取当前日期
    datenum = Integer.parseInt(datenum);
    var cuMonth = "";
    if (datenum > Integer.parseInt(billDate)) {
        if ((month + 1) < 10) {
            cuMonth = "0" + String.valueOf((month + 1));
        } else if (month == 12) {
            cuMonth = "01";
            year = year + 1;
        } else {
            cuMonth = String.valueOf((month + 1));
        }
    } else {
        if (month < 10) {
            cuMonth = "0" + String.valueOf(month);
        } else if (month == 12) {
            cuMonth = "01";
            year = year + 1;
        } else {
            cuMonth = String.valueOf(month);
        }
    }
    year = Integer.parseInt(year);
    cuMonth = cuMonth.replace(".0","")
    if(cuMonth.length == 1){
        cuMonth = "0" + cuMonth;
    }
    // 日期
    billAllData.setBillDate(yyyy_MM_dd_PARSER.parse(year + "-" + cuMonth + "-" + billDate));
    // 月
    billAllData.setBillMonth(year + "-" + cuMonth);
}

/**
 * ##########  抓取卡片  ##########
 */
function crawlCardInfo(cardNum) {
    try {
        logger.info("任务【{}】，招商银行开始抓取抓取卡片信息，包含卡号，用户名",context.get(LUA_TASK_KEY));
        var baseURL = "https://html.m.cmbchina.com/MobileHtml/creditcard/m_cardmanage/cm_queryenforcecard.aspx";
        var parametersMap = new HashMap();
        parametersMap.put("ClientNo", clientNo);
        parametersMap.put("Command", "");
        parametersMap.put("XmlReq", "");
        var html = cmbSubmitWithReTry(baseURL,parametersMap,accountAndcardNoAndbilloutAndbillHeaders(),"卡信息","post");
        storeDataToFile(context, html, cardNum + "_card.html");
        logger.info("任务【{}】，招商银行卡片信息，包含卡号，用户名完成",context.get(LUA_TASK_KEY));
        context.put("cardInfoHtml",html);
        //return html;
    } catch (e) {
        logger.error("taskKey={} 卡信息抓取异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    }
}

/**
 * 解析卡片信息
 */
function parseCardInfo (creditCardAllData) {
    try{
        var html = context.get("cardInfoHtml");
        logger.info("taskKey={} 开始解析卡信息", context.get(LUA_TASK_KEY));
        var doc = Jsoup.parse(html);
        var ps = doc.select(".ui-tb-l p:first-child");
        //是否开卡
        var isOpen = doc.select("div:nth-child(2) > label:nth-child(1)");
        var full_card_numList = new ArrayList();
        var username = "";
        for (var i = 0; i < isOpen.size(); i++) {
            if (!equals("未开卡",isOpen.get(i).text().trim())) {
                cardNo = ps.get(i).text().substring(0, ps.get(i).text().indexOf("（"));
                username = getGroupByIndex("（(.*)）", ps.get(i).text(), 1);
                full_card_numList.add(cardNo);
            }
        }
        logger.info("taskKey={} 银行卡数量为：{}", context.get(LUA_TASK_KEY), full_card_numList.size());
        creditCardAllData.setFullCardNum(list2String(full_card_numList));
        creditCardAllData.setCardNum(getCardNumList(full_card_numList));
        creditCardAllData.setNameOnCard(username);
        creditCardAllData.setOpenBank("");
        logger.info("任务【{}】，卡信息解析完成",context.get(LUA_TASK_KEY));
    }catch (e){
        logger.error("taskKey={} 卡信息解析异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    }
}

/**
 * 抓取账户
 */
function crawlAcountInfo(cardNum) {
    try {
        logger.info("任务【{}】，招商银行抓取账户信息，包含额度、账单日",context.get(LUA_TASK_KEY));
        var parametersMap = new HashMap();
        parametersMap.put("ClientNo", clientNo);
        parametersMap.put("XmlReq", "");
        parametersMap.put("Command", "");
        var html = cmbSubmitWithReTry(urls.ACOUNT_URL,parametersMap,accountAndcardNoAndbilloutAndbillHeaders(),"账户信息","post");
        storeDataToFile(context, html, cardNum + "_card_acount.html");
        //parseAccountInfo(html,creditCardAllData);
        context.put("acountInfoHtml",html);
    } catch (e) {
        logger.error("taskKey={} 账户信息抓取异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    }
}

/**
 * 解析账户信息
 */
function parseAccountInfo(creditCardAllData){
    logger.info("任务【{}】,开始解析账户信息",context.get(LUA_TASK_KEY))
    try{
        var html = context.get("acountInfoHtml");
        var doc = Jsoup.parse(html);
        // 可用信用额度 #cphBody_LimitAvail > a > span
        var availableLimit = doc.select("#cphBody_LimitAvail > a > span").text().trim().replaceAll("￥", "").replaceAll(",","");
        var availableLimitBD = new BigDecimal(availableLimit);
        // 信用额度 #cphBody_CreditLimit > a > span
        var creditLimit = doc.select("#cphBody_CreditLimit > a > span").text().trim().replaceAll("￥", "").replaceAll(",", "");
        // 可用取现额度 #cphBody_PreCashLimit > a > span
        var enchashmentImprest = doc.select("#cphBody_PreCashLimit > a > span").text().trim().replaceAll("￥", "").replaceAll(",", "");
        // 还款日（不包含年月） #cphBody_BQDQHQDate > a > span
        var repaymentDateTmp = doc.select("#cphBody_BQDQHQDate > a > span").text().trim();
        var repaymentDateArr = repaymentDateTmp.split("-");
        if (repaymentDateArr.length > 2) {
            repaymentDate = repaymentDateArr[2];
        }
        // 账单日（不包含年月）#cphBody_MonthBillDay > a > span
        billDate = doc.select("#cphBody_MonthBillDay > a > span").text().trim().replaceAll("日", "");
        if(StringUtils.isBlank(billDate)){
            logger.info("taskKey={},账单日获取失败，设置默认的账单日防止解析异常",context.get(LUA_TASK_KEY))
            billDate = "16";
        }
        // 可用信用额度
        creditCardAllData.setBalance(availableLimitBD);
        // 信用额度
        creditCardAllData.setCreditLimit(new BigDecimal(creditLimit));
        creditCardAllData.setCashBalance(null);
        // 可用现金额度
        creditCardAllData.setCashLimit(new BigDecimal(enchashmentImprest));
        logger.info("任务【{}】,账户信息解析完成",context.get(LUA_TASK_KEY))
    }catch (e){
        logger.error("taskKey={} 账户信息解析异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    }

}

/**
 * 抓取已出账单外层列表
 */
function crawlBillsInfo(cardNum) {
    logger.info("=========招商手机银行已出账单信息==========");
    try {
        logger.info("任务【{}】,已出账单外层列表开始抓取",context.get(LUA_TASK_KEY))
        var parametersMap = new HashMap();
        parametersMap.put("ClientNo", clientNo);
        parametersMap.put("Command", "");
        parametersMap.put("XmlReq", "");
        Thread.sleep(3000);
        //var html = post(urls.BILL_LIST_URL, parametersMap, accountAndcardNoAndbilloutAndbillHeaders());
        var html = cmbSubmitWithReTry(urls.BILL_LIST_URL,parametersMap,accountAndcardNoAndbilloutAndbillHeaders(),"已出账单信息","post");
        storeDataToFile(context, html, cardNum + "_bill_option.html");
        logger.info("任务【{}】,已出账单外层列表抓取完成",context.get(LUA_TASK_KEY))
        context.put("billsOptionsHtml",html);
    } catch (e) {
        logger.error("taskKey={} 已出账单抓取异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    }
}

/*
* 解析获取卡号信息
*/
function getCreditAccNo(){
    try{
        var html = context.get("billsOptionsHtml");
        logger.info("任务【{}】，开始解析卡号信息",context.get(LUA_TASK_KEY));
        var doc = Jsoup.parse(html);
        var creditAccNo = doc.select("#cphBody_ucCreditCardNo_CreditAccNo_selectCtl > option:first-child").val();
        logger.info("任务【{}】，解析卡号信息完成，creditAccNo={}",context.get(LUA_TASK_KEY),creditAccNo)
        return creditAccNo;
    }catch (e){
        logger.error("taskKey={} 解析卡号信息异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    }

}

/**
 * 获取未出账单首页信息
 */
function getBillHtmlWithOutFirstPage(cardNum) {
    try {
        logger.info("任务【{}】，未出账单第一页的外层列表开始抓取",context.get(LUA_TASK_KEY));
        var parametersMap = new HashMap();
        parametersMap.put("ClientNo", clientNo);
        parametersMap.put("Command", "");
        parametersMap.put("XmlReq", "");
        Thread.sleep(3000);
        //var html = post(urls.WEI_OUT_URL, parametersMap, accountAndcardNoAndbilloutAndbillHeaders());
        var html = cmbSubmitWithReTry(urls.WEI_OUT_URL,parametersMap,accountAndcardNoAndbilloutAndbillHeaders(),"未出账单第一页","post")
        storeDataToFile(context, html, cardNum + "_wei_out_1Page.html");// 第一页
        logger.info("任务【{}】，未出账单第一页的外层列表抓取完成",context.get(LUA_TASK_KEY));
        context.put("withOutFirstPageHtml",html);
    }catch (e){
        logger.error("taskKey={} 未出账单第一页的外层列表抓取异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    }
}

/**
 * 解析未出账单首页信息
 */
function parseBillHtmlWithOutFirstPage(){
    try {
        var html = context.get("withOutFirstPageHtml");
        logger.info("任务【{}】，未出账单第一页的外层列表开始解析",context.get(LUA_TASK_KEY));
        if (html.contains("未查询到未出账单记录！") || StringUtils.isBlank(html)) {
            return;
        }
        var cacheIdFirst = html.lastIndexOf("CacheID\", \"");
        var cacheIdEnd = html.indexOf("\");", cacheIdFirst);
        var cacheIdStart = cacheIdFirst + ("CacheID\", \"".length());
        var cacheID = html.substring(cacheIdStart, cacheIdEnd);
        var doc = Jsoup.parse(html);
        var option = doc.select("#cphBody_ucCreditAccNo_CreditAccNo_selectCtl > option:nth-child(1)").first();
        var creditAccNo = option.val();
        var o_tot_amnt = doc.select("#cphBody_DataCont > ul > li > a > span").text().trim();
        var newCharges = o_tot_amnt.replaceAll("￥", "").replaceAll(",", "");// 1,241.43
        // 获取当前页和总页
        var page = doc.select("#cphBody_NextBack_divPageNo > div > span.ui-money").text().trim();
        var pages = null;
        if (page !== "") {
            pages = page.split("/");
        }
        var buttons = doc.select("li[onclick^=javascript:GotoDetail]");
        context.put("o_tot_amnt",o_tot_amnt);
        context.put("cacheIdStart",cacheIdStart);
        context.put("cacheID",cacheID);
        context.put("creditAccNo",creditAccNo);
        context.put("newCharges",newCharges);
        context.put("pages",pages);
        context.put("buttons",buttons);
        context.put("page",page);
        logger.info("任务【{}】，未出账单第一页的外层列表解析完成",context.get(LUA_TASK_KEY));
    } catch (e){
        logger.error("taskKey={} 未出账单第一页的外层列表解析异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    }
}

/**
 * 未出账单第一页的recid
 * @param recID
 * @returns {string}
 */
function getRecid(recID){
    recID = getGroupByIndex("javascript:GotoDetail\\((.*)\\);", recID, 1);
    recID = "[" + recID + "]";
    var first = recID.indexOf("['");
    var as = recID.indexOf("']", first);
    var start1 = first + ("['".length());
    var recId = recID.substring(start1, as);
    return recId;
}

/**
 * 获取未出账单的详细信息
 */
function getDetailBillHtmlWithOutPage(cardNum,item,recId){
    try {
        // 消费
        logger.info("任务【{}】,未出账单第一页的详细信息抓取开始"+item,context.get(LUA_TASK_KEY));
        var parametersMap = new HashMap();
        parametersMap.put("ClientNo", clientNo);
        parametersMap.put("Command", "CMD_VIEWDETAIL");
        parametersMap.put("DeviceTAB", "TabDefault");
        parametersMap.put("CreditAccNo", context.get("creditAccNo"));
        parametersMap.put("PageNo", "1");
        parametersMap.put("RecID", recId);
        parametersMap.put("CacheID", context.get("cacheID"));
        parametersMap.put("Currency", "156");
        parametersMap.put("O_TOT_AMNT", context.get("o_tot_amnt"));
        Thread.sleep(2000);
        var billOutDetailHeader = new HashMap();
        billOutDetailHeader.put("User-Agent", params.USER_AGENT);
        billOutDetailHeader.put("Accept", params.ACCEPT);
        billOutDetailHeader.put("Host", "html.m.cmbchina.com");
        billOutDetailHeader.put("Origin", "https://html.m.cmbchina.com");
        billOutDetailHeader.put("Accept-Encoding", "gzip, deflate, br");
        billOutDetailHeader.put("Connection", "keep-alive");
        billOutDetailHeader.put("Content-Type", "application/x-www-form-urlencoded");
        billOutDetailHeader.put("Upgrade-Insecure-Requests", "1");
        //var htmlDetail = post(urls.WEI_OUT_DETAIL_URL, parametersMap, billOutDetailHeader);
        var htmlDetail = cmbSubmitWithReTry(urls.WEI_OUT_DETAIL_URL,parametersMap,billOutDetailHeader,"未出账单第一页详细信息","post")
        storeDataToFile(context, htmlDetail, cardNum + "_weiout_shopping_sheets_1Page_" + (item++) + "Item.html");
        context.put("itemFirst",item);
        logger.info("任务【{}】,未出账单第一页的详细信息抓取完成"+(item),context.get(LUA_TASK_KEY));
        context.put("DetailBillHtmlWithOutPageHtml",htmlDetail);
    } catch (e) {
        logger.error("taskKey={} 未出账单第一页的详细信息抓取异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    }
}

/**
 * 未出账单第一页的详细信息解析
 * @param htmlDetail
 * @param expense
 * @param cardNo
 * @param weiOutNewCharges
 * @param sumNewBalance
 * @param installment
 * @param i
 * @param billAllData
 * @param shopping_sheets
 */
function parseBillDetailHtmlWithOutFirst(expense,cardNo,weiOutNewCharges,sumNewBalance,installment,i,billAllData,shopping_sheets) {

    try {
        var htmlDetail = context.get("DetailBillHtmlWithOutPageHtml");
        logger.info("任务【{}】,未出账单第一页的详细信息解析开始"+i,context.get(LUA_TASK_KEY))
        var docDetail = Jsoup.parse(htmlDetail);
        var errorMsg = docDetail.select("#TEContent").text();
        if(htmlDetail.contains("由于设备所连IP地址发生变化或登录后长时间未操作，存在安全隐患。为保障您的账户安全，请重新登录")){
            logger.info("taskKey = {},该月停止抓取，错误信息：由于设备所连IP地址发生变化或登录后长时间未操作，存在安全隐患。为保障您的账户安全，请重新登录", context.get(LUA_TASK_KEY));
            return
        }
        if (errorMsg.contains("详细信息查询失败!")) {
            logger.info("taskKey = {} ,招商银行抓取：银行响应错误信息{} , 该月停止抓取", context.get(LUA_TASK_KEY), errorMsg);
            return;
        }
        // 交易日期 #cphBody_EFFECTIVE_DTE
        var tranDate = docDetail.select("#cphBody_EFFECTIVE_DTE").text().trim();

        // 交易摘要#cphBody_DESC_NAME
        var transRemark = docDetail.select("#cphBody_DESC_NAME").text().trim().replaceAll("\\s*", "");
        var recordDate = docDetail.select("#cphBody_POSTING_DTE").text().trim();
        //如果是尚未入账的
        var el = "[0-9]{4}-[0-9]{2}-[0-9]{2}";
        if (isMatch(el,recordDate)) {
            expense.setPostDate(yyyy_MM_dd_PARSER.parse(recordDate));
        } else {// 如果记账日期没有 就按交易日期的月份算
            expense.setPostDate(yyyy_MM_dd_PARSER.parse(tranDate));
        }
        if (((transRemark.contains("分期") && !transRemark.contains("DIY")) || transRemark.contains("苹果在线商店")) && remark_regx.test(transRemark)) {
            tranDate = getTheTransDate(transRemark,recordDate);
        }
        // 卡号末四位#cphBody_CARD_NMBR4
        cardNo = docDetail.select("#cphBody_CARD_NMBR4").text().trim();
        // 交易金额 #cphBody_TXN_CURR_AMT
        var transAmount = docDetail.select("#cphBody_TXN_CURR_AMT").text().trim().replaceAll("￥", "").replaceAll(",", "");
        if (equals(transAmount, "0.00")) {
            transAmount = docDetail.select("#cphBody_AMNT").text().trim().replaceAll("￥", "").replaceAll(",", "");
        }
        if(!transAmount.contains("-")){
            weiOutNewCharges =  weiOutNewCharges + Double.parseDouble(transAmount);
        }
        if(isMatch(el,recordDate)){
            sumNewBalance = sumNewBalance + Double.parseDouble(transAmount);
        }
        var transAmountBD = new BigDecimal(transAmount !== "" ? transAmount : "0");

        // 消费
        expense.setId(UUID.randomUUID().toString());
        expense.setDescription(transRemark);
        expense.setCardNum(cardNo);
        expense.setBalance(null);
        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(tranDate + " 00:00:00"));
        expense.setCurrencyType("CNY");
        expense.setAmountMoney(transAmountBD);
        // 未出账单的分类
        expense.setCategory(classify(transRemark,expense.getAmountMoney()));
        expense.setTransMethod("");//交易方式
        expense.setTransChannel("");//交易通道
        expense.setRemark("");//备注
        expense.setOppositeCardNo("");//对方卡号
        expense.setNameOnOppositeCard("");//对方持卡人
        expense.setOppositeBank("");//对方银行
        expense.setTransAddr("");
        i = ++i;
        context.put("theOrderIndexOut",i);
        expense.setOrderIndex(i);
        if(recordDate!= null && !recordDate.contains("尚未入账")){
            shopping_sheets.add(expense);
        }


        if ((transRemark.contains("分期") && !transRemark.contains("手续费") && !transRemark.contains("DIY")) || transRemark.contains("苹果在线商店")) {// 分期
            if (getFen(transRemark).size() > 1) {
                installment = new Installment();
                if(recordDate!= null && !recordDate.contains("尚未入账")){
                    billAllData.getInstallments().add(installment);//添加对象   但对象里面的值会自动同步
                }
                // 分期
                installment.setShoppingsheetId(UUID.randomUUID().toString());
                // 记账时间
                installment.setPostDate(yyyy_MM_dd_PARSER.parse(recordDate));
                // 交易时间
                installment.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(tranDate + " 00:00:00"));
                // 分期描述
                installment.setInstallmentDesc(transRemark);
                // 分期类型installment_type
                installment.setInstallmentType(installmentTypeChange(transRemark));
                installment.setTransferFee(null);
                installment.setTransferfeeDesc("")
                // 总分期数 total_month
                installment.setTotalMonth(getFen(transRemark).get(1));
                // 当前分期数
                installment.setCurrentMonth(getFen(transRemark).get(0));
                // 币种
                installment.setCurrencyType("CNY");
                // 本期金额
                installment.setAmountMoney(transAmountBD);
            }
        } else if (installment !=null && isNumExists(transRemark) && transRemark.contains("分期") && transRemark.contains("手续费") && !transRemark.contains("DIY")) {// 手续费
            // 分期手续费描述
            installment.setHandingfeeDesc(transRemark !== null ? transRemark : "");
            // 本期手续费
            installment.setHandingFee(transAmountBD);
        }
        logger.info("任务【{}】,未出账单第一页的详细信息解析结束"+i,context.get(LUA_TASK_KEY))
    } catch (e) {
        logger.error("taskKey={} 未出账单第一页的详细信息解析异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    }
}

/**
 * 获取未出账单的下一页的外层列表信息
 */
function getBillHtmlWithOutNextPage(currentPage,cardNum) {
    try {
        logger.info("任务【{}】,未出账单下一页的详细信息解析开始"+currentPage,context.get(LUA_TASK_KEY))
        var parametersMap = new HashMap();
        parametersMap.put("CreditAccNo", context.get("creditAccNo"));
        parametersMap.put("Currency", "156");
        parametersMap.put("PageNo", currentPage + "");
        parametersMap.put("Command", "");
        parametersMap.put("ClientNo", clientNo);
        parametersMap.put("DeviceTAB", "TabDefault");
        Thread.sleep(2000);
        var billOutNextPageHeader = new HashMap();
        billOutNextPageHeader.put("User-Agent", params.USER_AGENT);
        billOutNextPageHeader.put("Accept", params.ACCEPT);
        billOutNextPageHeader.put("Host", "html.m.cmbchina.com");
        billOutNextPageHeader.put("Origin", "https://html.m.cmbchina.com");
        billOutNextPageHeader.put("Accept-Encoding", "gzip, deflate, br");
        billOutNextPageHeader.put("Connection", "keep-alive");
        billOutNextPageHeader.put("Content-Type", "application/x-www-form-urlencoded");
        billOutNextPageHeader.put("Upgrade-Insecure-Requests", "1");
        //var pageNextXml = post(urls.WEI_OUT_DETAIL_URL, parametersMap, billOutNextPageHeader);
        var pageNextXml = cmbSubmitWithReTry(urls.WEI_OUT_DETAIL_URL,parametersMap,billOutNextPageHeader,"未出账单第"+currentPage+"页","post")
        storeDataToFile(context, pageNextXml, cardNum + "_wei_out_" + currentPage + "Page.html");
        logger.info("任务【{}】,未出账单下一页的详细信息解析结束"+currentPage,context.get(LUA_TASK_KEY))
        context.put("billHtmlWithOutNextPage",pageNextXml);
    } catch (e) {
        logger.error("taskKey={} 未出账单下一页的详细信息解析结束{}：{}", context.get(LUA_TASK_KEY),currentPage, e);
        statCaughtExceptionByJS(context, e);
    }
}

/**
 * 解析未出账单的下一页的外层列表信息
 * @param html
 * @param currentPage
 */
function parseBillHtmlWithOutNextPage(currentPage) {
    try {
        var html = context.get("billHtmlWithOutNextPage");
        logger.info("任务【{}】,开始解析未出账单的下一页的外层列表信息"+currentPage,context.get(LUA_TASK_KEY))
        var pageNextDoc = Jsoup.parse(html);
        var buttonsPage = pageNextDoc.select("li[onclick^=javascript:GotoDetail]");
        var item1 = (10 * (currentPage - 1));
        context.put("buttonsPage",buttonsPage);
        context.put("item1",item1);
        logger.info("任务【{}】,解析未出账单的下一页的外层列表信息完成"+currentPage,context.get(LUA_TASK_KEY))
    } catch (e) {
        logger.error("taskKey={} 解析未出账单的下一页的外层列表信息异常{}：{}", context.get(LUA_TASK_KEY),currentPage, e);
        statCaughtExceptionByJS(context, e);
    }
}

/**
 * 获取未出账单下一页的recid
 * @param recID
 * @returns {string}
 */
function getRecid2(recID){
    logger.info("recID = {} ", recID);
    recID = getGroupByIndex("javascript:GotoDetail\\((.*)\\);", recID, 1);
    recID = "[" + recID + "]";
    var first = recID.indexOf("['");
    var as = recID.indexOf("']", first);
    var start1 = first + ("['".length());
    var news = recID.substring(start1, as);
    return news;
}

/**
 * 获取未出账单下一页的详细信息
 */
function getBillDetailHtmlNextPage(currentPage,news,cardNum,item1) {
    try {
        var item = ++item1;
        logger.info("任务【{}】,未出账单的下一页的详细信息开始抓取"+currentPage+"_"+item,context.get(LUA_TASK_KEY))
        var parametersMap = new HashMap();
        parametersMap.put("ClientNo", clientNo);
        parametersMap.put("Command", "CMD_VIEWDETAIL");
        parametersMap.put("DeviceTAB", "TabDefault");
        parametersMap.put("CreditAccNo", context.get("creditAccNo"));
        parametersMap.put("PageNo", currentPage + "");
        parametersMap.put("RecID", news);
        parametersMap.put("CacheID", context.get("cacheID"));
        parametersMap.put("Currency", "156");
        parametersMap.put("O_TOT_AMNT", context.get("o_tot_amnt"));
        Thread.sleep(2000);
        var pageHeader = new HashMap();
        pageHeader.put("Accept", params.ACCEPT);
        pageHeader.put("Host", "html.m.cmbchina.com");
        pageHeader.put("Origin", "https://html.m.cmbchina.com");
        pageHeader.put("Referer", "https://html.m.cmbchina.com/MobileHtml/CreditCard/M_AccountManage/am_QueryReckoningSurveyNot.aspx");
        pageHeader.put("User-Agent", params.USER_AGENT);
        //var htmlDetail = post(urls.WEI_OUT_DETAIL_URL, parametersMap, pageHeader);
        var htmlDetail = cmbSubmitWithReTry(urls.WEI_OUT_DETAIL_URL,parametersMap,pageHeader,"详细信息查询","post")
        storeDataToFile(context, htmlDetail, cardNum + "_weiout_bill_shopping_sheets_" + currentPage + "Page_" + item + "Item.html");
        logger.info("任务【{}】,未出账单的下一页的详细信息抓取完成"+currentPage+"_"+item,context.get(LUA_TASK_KEY))
        context.put("itemNext",item);
        context.put("billDetailHtmlNextPage",htmlDetail);
    } catch (e) {
        logger.error("taskKey={} 未出账单的下一页的详细信息抓取异常{}：{}", context.get(LUA_TASK_KEY),item, e);
        statCaughtExceptionByJS(context, e);
    }
}

/**
 * 解析未出账单的下一页的详细信息
 */
function parseBillDetailHtmlNextPage(expense,shopping_sheets,sumNewBalance,weiOutNewCharges,installment,billAllData,cardNo,i) {
    try {
        var htmlDetail = context.get("billDetailHtmlNextPage");
        logger.info("任务【{}】,未出账单下一页的详细信息解析开始",context.get(LUA_TASK_KEY));
        var docDetail = Jsoup.parse(htmlDetail);
        var errorMsg = docDetail.select("#TEContent").text();
        if (errorMsg.contains("详细信息查询失败!")) {
            logger.info("任务{},招商银行抓取：银行响应错误信息{}，该月停止抓取", context.get(LUA_TASK_KEY), errorMsg);
            return;
        }

        // 交易日期 #cphBody_EFFECTIVE_DTE
        var tranDate = docDetail.select("#cphBody_EFFECTIVE_DTE").text().trim();
        // 交易摘要#cphBody_DESC_NAME
        var transRemark = docDetail.select("#cphBody_DESC_NAME").text().trim().replaceAll("\\s*", "");
        // 记账日期#cphBody_POSTING_DTE 2017-05-23
        var recordDate = docDetail.select("#cphBody_POSTING_DTE").text().trim();
        var eL = "[0-9]{4}-[0-9]{2}-[0-9]{2}";
        if(!equals(recordDate,"") && !equals(tranDate,"")){
            if (isMatch(eL,recordDate)) {
                expense.setPostDate(yyyy_MM_dd_PARSER.parse(recordDate));
            } else {// 如果记账日期没有 就按交易日期的月份算
                expense.setPostDate(yyyy_MM_dd_PARSER.parse(tranDate));
            }
        }
        if (((transRemark.contains("分期") && !transRemark.contains("DIY")) || transRemark.contains("苹果在线商店")) && remark_regx.test(transRemark)) {
            tranDate = getTheTransDate(transRemark,recordDate);
        }

        // 交易金额 #cphBody_TXN_CURR_AMT
        var transAmount = docDetail.select("#cphBody_TXN_CURR_AMT").text().trim().replaceAll("￥", "").replaceAll(",", "");
        if(isMatch(eL,recordDate)){
            sumNewBalance = sumNewBalance + Double.parseDouble(transAmount);
        }
        if(!transAmount.contains("-") && !equals(transAmount,"")){
            weiOutNewCharges =  weiOutNewCharges + Double.parseDouble(transAmount);
        }
        var transAmountBD = null;
        if (transAmount !== "") {
            transAmountBD = new BigDecimal(transAmount);
        }
        // 消费
        expense.setId(UUID.randomUUID().toString());
        expense.setDescription(transRemark);
        expense.setCardNum(cardNo);
        if(!equals(tranDate,"")){
            expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(tranDate + " 00:00:00"));
        }
        expense.setCurrencyType("CNY");
        expense.setAmountMoney(transAmountBD);
        expense.setBalance(null);
        // 未出账单的分类
        expense.setCategory(classify(transRemark,expense.getAmountMoney()));
        expense.setTransMethod("");//交易方式
        expense.setTransChannel("");//交易通道
        expense.setRemark("");//备注
        expense.setOppositeCardNo("");//对方卡号
        expense.setNameOnOppositeCard("");//对方持卡人
        expense.setOppositeBank("");//对方银行
        expense.setTransAddr("");
        i = ++i;
        context.put("orderIndexNext",i);
        expense.setOrderIndex(i);
        shopping_sheets.add(expense);

        if ((transRemark.contains("分期")
                && !transRemark.contains("手续费")
                && !transRemark.contains("DIY")) || transRemark.contains("苹果在线商店")) {// 分期
            if (getFen(transRemark).size() > 1) {
                installment = new Installment();
                billAllData.getInstallments().add(installment);//添加对象   但对象里面的值会自动同步
                // 分期
                installment.setShoppingsheetId(UUID.randomUUID().toString());
                // 记账时间
                installment.setPostDate(yyyy_MM_dd_PARSER.parse(recordDate));
                // 交易时间
                installment.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(tranDate + " 00:00:00"));
                // 分期描述
                installment.setInstallmentDesc(transRemark);
                // 分期类型installment_type
                installment.setInstallmentType(installmentTypeChange(transRemark));
                installment.setTransferFee(null);
                installment.setTransferfeeDesc("")
                // 总分期数 total_month
                installment.setTotalMonth(getFen(transRemark).get(1));
                // 当前分期数
                installment.setCurrentMonth(getFen(transRemark).get(0));
                // 币种
                installment.setCurrencyType("CNY");
                // 本期金额
                installment.setAmountMoney(transAmountBD);
            }
        }else if (installment !=null && isNumExists(transRemark) && transRemark.contains("分期") && transRemark.contains("手续费") && !transRemark.contains("DIY")) {// 手续费
            // 分期手续费描述
            installment.setHandingfeeDesc(transRemark);
            // 本期手续费
            installment.setHandingFee(transAmountBD);
        }
        logger.info("任务【{}】,未出账单下一页的详细信息解析完成",context.get(LUA_TASK_KEY));
    } catch (e){
        logger.error("taskKey={} 未出账单下一页的详细信息解析异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    }
}

/*
* 抓取单月已出账单汇总信息
*/
function getDetailBillTotalMsg(creditAccNo, oneMonth,month,cardNum) {
    try{
        logger.info("任务【{}】,抓取已出账单汇总信息开始",context.get(LUA_TASK_KEY))
        var mon = "";
        if (String.valueOf(month).length() == 1) {
            mon = "0" + String.valueOf(month);
        } else {
            mon = String.valueOf(month);
        }
        var parameters = new HashMap();
        parameters.put("ClientNo", clientNo);
        parameters.put("Currency", "156");
        parameters.put("CreditAccNo", creditAccNo);
        parameters.put("ColumnOfDate", oneMonth);
        parameters.put("Command", "CMD_DETAIL");
        parameters.put("DeviceTAB", "TabDefault");
        // 对账单详细信息页请求地址
        var billDetailHeader = new HashMap();
        billDetailHeader.put("User-Agent", params.USER_AGENT);
        billDetailHeader.put("Accept", params.ACCEPT);
        billDetailHeader.put("Host", "html.m.cmbchina.com");
        billDetailHeader.put("Origin", "https://html.m.cmbchina.com");
        billDetailHeader.put("Referer", "https://html.m.cmbchina.com/MobileHtml/CreditCard/M_AccountManage/am_QueryReckoningList.aspx");
        billDetailHeader.put("Accept-Encoding", "gzip, deflate, br");
        billDetailHeader.put("Connection", "keep-alive");
        billDetailHeader.put("Content-Type", "application/x-www-form-urlencoded");
        billDetailHeader.put("Upgrade-Insecure-Requests", "1");
        //var html = post(urls.BILL_URL, parameters, billDetailHeader);
        var bnenqihtml = cmbSubmitWithReTry(urls.BILL_URL,parameters,billDetailHeader,"已出账单明细月份"+oneMonth,"post");
        storeDataToFile(context, bnenqihtml, cardNum + "_bill_" + mon + "mon_benqiPage.html");
        logger.info("任务【{}】,抓取已出账单汇总信息结束",context.get(LUA_TASK_KEY))
        context.put("detailBillTotalMsgHtml",bnenqihtml);
    }catch (e){
        logger.error("taskKey={},抓取已出账单汇总信息异常{}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    }
}

/**
 * 解析已出账单汇总信息
 * @param html
 * @param billAllData
 */
function parseDetailBillTotalMsg(billAllData,q) {
    try{
        var html = context.get("detailBillTotalMsgHtml");
        logger.info("任务【{}】，解析已出账单汇总信息开始",context.get(LUA_TASK_KEY))
        var doc = Jsoup.parse(html);
        var errorMsg = doc.select("#TEContent").text();
        if(html.contains("由于设备所连IP地址发生变化或登录后长时间未操作，存在安全隐患。为保障您的账户安全，请重新登录")){
            logger.info("taskKey = {},该月停止抓取，错误信息：由于设备所连IP地址发生变化或登录后长时间未操作，存在安全隐患。为保障您的账户安全，请重新登录", context.get(LUA_TASK_KEY));
            return
        }
        if (StringUtils.isNotBlank(errorMsg)) {
            logger.info("taskKey = {},该月停止抓取，错误信息：{}", context.get(LUA_TASK_KEY), errorMsg);
            return;
        }

        // 本期还款金额 #cphBody_liBQYHJE > a > span #cphBody_AccInfoDT >
        var sumBalance = doc.select("#cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(1) > a > span").text()
            .trim().replaceAll("￥", "").replaceAll(",", "");
        q = Integer.parseInt(q);
        if (q == 0) {
            logger.info("q={}", q);
            contextMap.put("newBalance", sumBalance);
        }
        // 最低还款金额 #cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(2) >
        var minRepayment = doc
            .select("#cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(2) > a:nth-child(1) > span:nth-child(1)")
            .text().trim().replaceAll("￥", "").replaceAll(",", "");

        // 上期账单金额
        var lastBalance = doc
            .select("#cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(3) > a:nth-child(1) > span:nth-child(1)")
            .text().trim().replaceAll("￥", "").replaceAll(",", "");

        // 上期还款金额
        var lastRepayment = doc
            .select("#cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(4) > a:nth-child(1) > span:nth-child(1)")
            .text().trim().replaceAll("￥", "").replaceAll(",", "");

        // 本期账单金额
        var newBalance = doc.select("#cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(5) > a > span").text()
            .trim().replaceAll("￥", "").replaceAll(",", "");

        // 循环利息   #cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(7) > a > span
        var interest = doc.select("#cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(7) > a:nth-child(1) > span:nth-child(1)").text().trim().replaceAll("￥", "").replaceAll(",", "");
        // 最低还款金额
        billAllData.setMinPayment(minRepayment !== "" ? new BigDecimal(minRepayment) : new BigDecimal(0));
        // 本期应还金额
        billAllData.setNewBalance(sumBalance !== "" ? new BigDecimal(sumBalance) : new BigDecimal(0));
        // 上期账单金额
        billAllData.setLastBalance(lastBalance !== "" ? new BigDecimal(lastBalance) : new BigDecimal(0));
        // 上期还款金额
        billAllData.setLastPayment(lastRepayment !== "" ? new BigDecimal("-" + lastRepayment) : new BigDecimal(0));
        // 本期账单金额
        billAllData.setNewCharges(newBalance !== "" ? new BigDecimal(newBalance) : new BigDecimal(0));
        // 循环利息
        billAllData.setInterest(interest !== "" ? new BigDecimal(interest) : new BigDecimal(0));
        logger.info("任务【{}】，解析已出账单汇总信息完成",context.get(LUA_TASK_KEY));
    }catch (e){
        logger.error("taskKey={},解析已出账单汇总信息异常{}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    }
}

/**
 * 已出账单明细抓取
 */
function getDetailBillByDate(creditAccNo, oneMonth, month,  cardNum) {
    logger.info("招商银行抓取年月: {} ", oneMonth);
    try {
        logger.info("任务【{}】，已出账单明细抓取开始",context.get(LUA_TASK_KEY))
        var mon = "";
        if (String.valueOf(month).length() == 1) {
            mon = "0" + String.valueOf(month);
        } else {
            mon = String.valueOf(month);
        }
        //抓取改期账单的详细页
        var parameters = new HashMap();
        parameters.put("ClientNo", clientNo);
        parameters.put("Currency", "156");
        parameters.put("CreditAccNo", creditAccNo);
        parameters.put("ColumnOfDate", oneMonth);
        parameters.put("DeviceTAB", "TabDefault");

        // 对账单详细信息页请求地址
        var billHeader = new HashMap();
        billHeader.put("User-Agent", params.USER_AGENT);
        billHeader.put("Accept", params.ACCEPT);
        billHeader.put("Host", "html.m.cmbchina.com");
        billHeader.put("Origin", "https://html.m.cmbchina.com");
        billHeader.put("Referer", "https://html.m.cmbchina.com/MobileHtml/creditcard/m_accountmanage/am_queryreckoningsurvey.aspx");
        billHeader.put("Accept-Encoding", "gzip, deflate, br");
        billHeader.put("Connection", "keep-alive");
        billHeader.put("Content-Type", "application/x-www-form-urlencoded");
        billHeader.put("Upgrade-Insecure-Requests", "1");

        var billhtml = cmbSubmitWithReTry(urls.BILL_URL,parameters,billHeader,"对账单详细页"+mon,"post");
        //html = post(urls.BILL_URL, parameters, billHeader);
        storeDataToFile(context, billhtml, cardNum + "_bill_shopping_sheets_" + mon + "mon_1Page.html");
        logger.info("任务【{}】，已出账单明细页抓取完成",context.get(LUA_TASK_KEY));
        context.put("detailBillFirstHtml",billhtml);
    } catch (e) {
        logger.error("taskKey={} 已出账单抓取异常：{},招商银行抓取年月: {}", context.get(LUA_TASK_KEY), e, oneMonth);
        statCaughtExceptionByJS(context, e);
    }
}

/**
 * 解析已出账单明细页第一页信息
 * @param html
 * @param year
 * @param billAllData
 * @param shopping_sheets
 * @param installments
 * @returns {string|*}
 */
function parseBillHtmlFirstPage(year, billAllData, shopping_sheets, installments,month,i){
    try{
        var html = context.get("detailBillFirstHtml");
        logger.info("任务【{}】，开始解析已出账单第一页的信息",context.get(LUA_TASK_KEY))
        var doc = Jsoup.parse(html);
        billMonth = doc.select("#cphBody_DataContent1 > ul:nth-child(1) > li:nth-child(1) > a:nth-child(1) > span:nth-child(1)").text().trim().replaceAll("-", "").substring(4, 6);
        payMonth = doc.select("#cphBody_DataContent1 > ul:nth-child(1) > li:nth-child(2) > a:nth-child(1) > span:nth-child(1)").text().trim();
        if (payMonth.matches("[0-9]{4}-[0-9]{2}-[0-9]{2}")) {
            payMonth = payMonth.replaceAll("-", "").substring(4, 6);
        }
        logger.info("{},账单月{},还款月{}", context.get(LUA_TASK_KEY),billMonth, payMonth);
        var errorMsg = doc.select("#TEContent").text();

        if (StringUtils.isNotBlank(errorMsg)) {
            logger.info("{ } 招商银行抓取：银行响应错误信息{}，该月停止抓取", context.get(LUA_TASK_KEY), errorMsg);
            return;
        }


        var buttons = doc.select("li[onclick^=javascript:QueryListDetail]");
        // 获取总页数
        var pageSum = doc.select("#cphBody_NextBack_divPageNo > div > span.ui-money").text().trim();

        var installment = null;
        lastInstallList = null;
        for (var item = 0; item < buttons.size(); item++) {
            var expenseList = getList(buttons, item);
            if(expenseList.size()<8){
                continue;
            }
            // 消费
            var expense = new Expense();
            // 交易金额
            var transAmount = expenseList.get(3).trim().replaceAll(",", "").replaceAll("￥", "");
            var amount_money = null;

            // 交易摘要
            var description = expenseList.get(2).trim().replaceAll("\\s*", "");

            expense.setDescription(description);
            expense.setId(UUID.randomUUID().toString());

            var record_date_exp = "";
            if (StringUtils.isNotBlank(expenseList.get(1).trim())) {
                if (1 == month && equals("12", expenseList.get(1).trim().split("-")[0])) {
                    // 记账日期
                    record_date_exp = (Integer.parseInt(year) - 1) + "-" + expenseList.get(1).trim()
                    expense.setPostDate(yyyy_MM_dd_PARSER.parse(record_date_exp));
                } else {
                    // 记账日期
                    record_date_exp = year + "-" + expenseList.get(1).trim();
                    expense.setPostDate(yyyy_MM_dd_PARSER.parse(record_date_exp));
                }
            }

            if((((description.contains("分期") && !description.contains("DIY")) || description.contains("苹果在线商店")) && !description.contains("￥")) && remark_regx.test(description)){
                var trans_date = getTheTransDate(description,record_date_exp);
                expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(trans_date + " 00:00:00"));
            }else{
                if(StringUtils.isNotBlank(expenseList.get(6).trim())){
                    if (1 == month && equals("12", expenseList.get(6).trim().split("-")[0])) {
                        // 交易日期
                        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year) - 1) + "-" + expenseList.get(6).trim() + " 00:00:00"));
                    } else {
                        // 交易日期
                        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(year + "-" + expenseList.get(6).trim() + " 00:00:00"));
                    }
                } else {
                    if (1 == month && equals("12", expenseList.get(1).trim().split("-")[0])) {
                        // 交易日期
                        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year) - 1) + "-" + expenseList.get(1).trim() + " 00:00:00"));
                    } else {
                        // 交易日期
                        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(year + "-" + expenseList.get(1).trim() + " 00:00:00"));
                    }
                }
            }


            // 交易地点
            expense.setTransAddr(expenseList.get(5).trim());
            if (transAmount !== "") {
                amount_money = new BigDecimal(transAmount);
                // 交易金额
                expense.setAmountMoney(amount_money);
            } else {
                //交易金额
                expense.setAmountMoney(new BigDecimal(0));
            }
            // 交易币种
            expense.setCurrencyType("CNY");
            expense.setBalance(null);
            // 卡号末四位
            expense.setCardNum(expenseList.get(4).trim());
            // 已出账单的分类
            expense.setCategory(classify(description,expense.getAmountMoney()));
            expense.setTransMethod("");//交易方式
            expense.setTransChannel("");//交易通道
            expense.setRemark("");//备注
            expense.setOppositeCardNo("");//对方卡号
            expense.setNameOnOppositeCard("");//对方持卡人
            expense.setOppositeBank("");//对方银行

            expense.setOrderIndex(++i);
            shopping_sheets.add(expense);
            billAllData.setShoppingSheets(shopping_sheets);

            // 判断摘要是分期
            if (((description.contains("分期") && !description.contains("DIY")) || description.contains("苹果在线商店")) && !description.contains("￥") && !description.contains("手续费")) {// 分期
                //判断如果最后一条是分期数据。就把这个expenseList放到下一页取处理

                if(item == buttons.size()-1){
                    lastInstallList = expenseList;
                }else if (getFen(description).size() > 1) {
                    installment = new Installment();
                    billAllData.getInstallments().add(installment);//添加对象   但对象里面的值会自动同步
                    // 分期
                    installment.setShoppingsheetId(UUID.randomUUID().toString());
                    // 记账时间 post_date
                    var record_date = "";
                    if (StringUtils.isNotBlank(expenseList.get(1).trim())) {
                        if (1 == month && equals("12", expenseList.get(1).trim().split("-")[0])) {
                            // 记账日期
                            record_date = (Integer.parseInt(year) - 1) + "-" + expenseList.get(1).trim();
                            installment.setPostDate(yyyy_MM_dd_PARSER.parse(record_date));
                        } else {
                            // 记账日期
                            record_date = (Integer.parseInt(year)) + "-" + expenseList.get(1).trim();
                            installment.setPostDate(yyyy_MM_dd_PARSER.parse(record_date));
                        }

                    }
                    // 交易时间 trans_date
                    if (StringUtils.isNotBlank(expenseList.get(0).trim())) {
                        if(remark_regx.test(description)){
                            var trans_date = getTheTransDate(description,record_date);
                            installment.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(trans_date + " 00:00:00"));
                        }else{
                            if (1 == month && equals("12", expenseList.get(0).trim().split("-")[0])) {
                                // 交易日期
                                installment
                                    .setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year) - 1) + "-" + expenseList.get(0).trim() + " 00:00:00"));
                            } else {
                                // 交易日期
                                installment
                                    .setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year)) + "-" + expenseList.get(0).trim() + " 00:00:00"));
                            }
                        }
                    }
                    // 分期描述
                    installment.setInstallmentDesc(description);
                    // 分期类型installment_type
                    installment.setInstallmentType(installmentTypeChange(description));
                    // 总分期数 total_month
                    installment.setTotalMonth(getFen(description).get(1));
                    // 当前分期数
                    installment.setCurrentMonth(getFen(description).get(0));
                    // 币种
                    installment.setCurrencyType("CNY");
                    installment.setTransferFee(null);
                    installment.setTransferfeeDesc("")
                    // 本期金额
                    installment.setAmountMoney(amount_money);
                    //
                    if((item+ 1) != buttons.size()){
                        // 分期
                        if ((item + 1) <= buttons.size()) {// 下一条是分期手续费
                            var installmentList = getList(buttons, (item + 1));
                            if (installmentList.size() == 8) {
                                // 分期描述
                                var handingFeeDesc = installmentList.get(2).trim().replaceAll("\\s*", "");
                                logger.info("{},这是下一条,手续费描述:{}", context.get(LUA_TASK_KEY), handingFeeDesc);
                                var handingFee = installmentList.get(3).trim().replaceAll(",", "").replaceAll("￥", "");
                                if(handingFeeDesc.matches(".*[0-9]{2,}-[0-9]{2,}")){
                                    logger.info("1019 -- {},分期里面的数字:handingFeeDesc[0]=:{}/handingFeeDesc[1]=:{}/description[0]=:{}/description[1]=:{}",context.get(LUA_TASK_KEY),getFen(handingFeeDesc).get(0),getFen(handingFeeDesc).get(1),getFen(description).get(0),getFen(description).get(1));
                                    if ( getFen(handingFeeDesc).get(0) == getFen(description).get(0)) {
                                        installment.setHandingFee(!equals("",handingFee) ? new BigDecimal(handingFee) : new BigDecimal(0));
                                        installment.setHandingfeeDesc(handingFeeDesc);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        logger.info("任务【{}】,已出账单第一页解析完成",context.get(LUA_TASK_KEY))
        context.put("cmb_count_j",i);
        return pageSum;
    }catch (e){
        logger.error("taskKey={} 已出账单第一页信息抓取异常: {}", context.get(LUA_TASK_KEY), e);
        statCaughtExceptionByJS(context, e);
    } finally {
        if (installments.size() != 0) {
            billAllData.setInstallments(installments);
        }
    }
}

/**
 * 获取已出账单明细页下一页信息
 * @param creditAccNo
 * @param oneMonth
 * @param month
 * @param cardNum
 * @param currentPage
 */
function getBillHtmlNextHtml(creditAccNo, oneMonth, month,  cardNum,currentPage){
    try {
        logger.info("任务【{}】，已出账单明细第{}页，开始抓取",context.get(LUA_TASK_KEY),currentPage)
        var mon = "";
        if (String.valueOf(month).length() == 1) {
            mon = "0" + String.valueOf(month);
        } else {
            mon = String.valueOf(month);
        }
        var url = "https://html.m.cmbchina.com/MobileHtml/CreditCard/M_AccountManage/am_QueryReckoningList.aspx";
        var map = new HashMap();
        map.clear();
        map.put("CreditAccNo", creditAccNo);
        map.put("Currency", "156");
        map.put("ColumnOfDate", oneMonth);
        map.put("PageNo", currentPage + "");
        map.put("Command", "");
        map.put("ClientNo", clientNo);
        map.put("DeviceTAB", "TabDefault");
        Thread.sleep(3000);
        var billHeaders = new HashMap();
        billHeaders.put("Referer", "https://html.m.cmbchina.com/MobileHtml/CreditCard/M_AccountManage/am_QueryReckoningList.aspx");
        billHeaders.put("Host", "html.m.cmbchina.com");
        billHeaders.put("Origin", "https://html.m.cmbchina.com");
        billHeaders.put("Accept", params.ACCEPT);
        billHeaders.put("User-Agent", params.USER_AGENT);

        //var currentPageXMl = post(url, map, billHeaders);
        var currentPageXMl = cmbSubmitWithReTry(url,map,billHeaders,"对账单详细页月份"+mon+"页数"+currentPage,"post");
        storeDataToFile(context, currentPageXMl, cardNum + "_bill_shopping_sheets_" + mon + "mon_" + currentPage + "Page.html");
        logger.info("任务【{}】，已出账单明细第{}页，抓取完成",context.get(LUA_TASK_KEY),currentPage);
        context.put("billHtmlNextHtml",currentPageXMl);
    }catch (e){
        logger.error("taskKey={} 已出账单明细第{}页，抓取异常{}", context.get(LUA_TASK_KEY),currentPage, e);
        statCaughtExceptionByJS(context, e);
    }


}

/**
 * 解析下一页信息已出账单详细信息
 * @param html
 * @param year
 * @param billAllData
 * @param shopping_sheets
 * @param installments
 * @param currentPage
 */
function parseBillHtmlNextPage(year, month,billAllData, shopping_sheets, installments,currentPage,i) {
    try{
        var html = context.get("billHtmlNextHtml");
        logger.info("任务【{}】，已出账单明细第{}页，解析开始",context.get(LUA_TASK_KEY),currentPage);
        var currentPageDoc = Jsoup.parse(html);
        var buttonCurrentPage = currentPageDoc.select("li[onclick^=javascript:QueryListDetail]");
        var install = null;
        for (var item = 0; item < buttonCurrentPage.size(); item++) {
            var currentPagelist = null;
            if(lastInstallList!=null && lastInstallList.size()>=1){
                //说明上一条是分期数据，处理这条数据之后再处理处理之后的
                item--;
                currentPagelist = lastInstallList;
                lastInstallList = null;
            }else{
                currentPagelist = getList(buttonCurrentPage, item);
            }
            if(currentPagelist.size()<8){
                continue;
            }
            // 消费
            var expense = new Expense();
            // 交易金额
            var transAmount = currentPagelist.get(3).trim().replaceAll(",", "").replaceAll("￥", "");
            var amount_money = null;
            // 交易摘要
            var description = currentPagelist.get(2).trim().replaceAll("\\s*", "");
            expense.setDescription(description);
            expense.setId(UUID.randomUUID().toString());
            var record_date_exp = "";
            if (StringUtils.isNotBlank(currentPagelist.get(1).trim())) {
                if (1 == month && equals("12", currentPagelist.get(1).trim().split("-")[0])) {
                    // 记账日期
                    record_date_exp = (Integer.parseInt(year) - 1) + "-" + currentPagelist.get(1).trim()
                    expense.setPostDate(yyyy_MM_dd_PARSER.parse(record_date_exp));
                } else {
                    // 记账日期
                    record_date_exp = year + "-" + currentPagelist.get(1).trim()
                    expense.setPostDate(yyyy_MM_dd_PARSER.parse(record_date_exp));
                }
            }
            if(((description.contains("分期") && !description.contains("DIY")) || description.contains("苹果在线商店") && !description.contains("￥")) && remark_regx.test(description)){
                var trans_date = getTheTransDate(description,record_date_exp);
                expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(trans_date + " 00:00:00"));
            }else{
                if (StringUtils.isNotBlank(currentPagelist.get(6).trim())) {
                    if (1 == month && equals("12", currentPagelist.get(6).trim().split("-")[0])) {
                        // 交易日期
                        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year) - 1) + "-" + currentPagelist.get(6).trim() + " 00:00:00"));
                    } else {
                        // 交易日期
                        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(year + "-" + currentPagelist.get(6).trim() + " 00:00:00"));
                    }
                } else {
                    if (1 == month && equals("12", currentPagelist.get(1).trim().split("-")[0])) {
                        // 交易日期
                        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year) - 1) + "-" + currentPagelist.get(1).trim() + " 00:00:00"));
                    } else {
                        // 交易日期
                        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(year + "-" + currentPagelist.get(1).trim() + " 00:00:00"));
                    }
                }
            }

            // 交易地点
            expense.setTransAddr(currentPagelist.get(5).trim());
            if (transAmount !== "") {
                amount_money = new BigDecimal(transAmount)
                // 交易金额
                expense.setAmountMoney(new BigDecimal(transAmount));
            } else {
                expense.setAmountMoney(new BigDecimal(0));
            }
            // 交易币种
            expense.setCurrencyType("CNY");
            expense.setBalance(null);
            // 卡号末四位
            expense.setCardNum(currentPagelist.get(4).trim());
            // 已出账单的分类
            expense.setCategory(classify(description,expense.getAmountMoney()));
            expense.setOrderIndex(++i);

            expense.setTransMethod("");//交易方式
            expense.setTransChannel("");//交易通道
            expense.setRemark("");//备注
            expense.setOppositeCardNo("");//对方卡号
            expense.setNameOnOppositeCard("");//对方持卡人
            expense.setOppositeBank("");//对方银行

            shopping_sheets.add(expense);
            billAllData.setShoppingSheets(shopping_sheets);

            // 判断摘要是分期
            if (((description.contains("分期") && !description.contains("DIY")) || description.contains("苹果在线商店")) && !description.contains("￥") && !description.contains("手续费")) {// 分期
                if(item == buttonCurrentPage.size()-1){
                    //分页中的最后一条如果是分期数据，同样放到下一页处理
                    lastInstallList = currentPagelist;
                }else if (getFen(description).size() > 1) {
                    install = new Installment();
                    billAllData.getInstallments().add(install);//添加对象   但对象里面的值会自动同步
                    // 分期
                    install.setShoppingsheetId(UUID.randomUUID().toString());
                    // 记账时间 post_date
                    var record_date = "";
                    if (StringUtils.isNotBlank(currentPagelist.get(1).trim())) {
                        if (1 == month && equals("12", currentPagelist.get(1).trim().split("-")[0])) {
                            // 记账日期
                            record_date = (Integer.parseInt(year) - 1) + "-" + currentPagelist.get(1).trim()
                            print(record_date)
                            install.setPostDate(yyyy_MM_dd_PARSER.parse(record_date));
                        } else {
                            // 记账日期
                            record_date = (Integer.parseInt(year)) + "-" + currentPagelist.get(1).trim();
                            print(record_date)
                            install.setPostDate(yyyy_MM_dd_PARSER.parse(record_date));
                        }
                    }
                    print(record_date)
                    // 交易时间 trans_date
                    if (StringUtils.isNotBlank(currentPagelist.get(0).trim())) {
                        if(remark_regx.test(description)){
                            var trans_date = getTheTransDate(description,record_date);
                            install.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(trans_date + " 00:00:00"));
                        }else{
                            if (1 == month && equals("12", currentPagelist.get(0).trim().split("-")[0])) {
                                // 交易日期
                                install.setTransDate(
                                    yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year) - 1) + "-" + currentPagelist.get(0).trim() + " 00:00:00"));
                            } else {
                                // 交易日期
                                install.setTransDate(
                                    yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year) - 1) + "-" + currentPagelist.get(0).trim() + " 00:00:00"));
                            }
                        }
                    }
                    // 分期描述
                    install.setInstallmentDesc(description);
                    // 分期类型installment_type
                    install.setInstallmentType(installmentTypeChange(description));
                    install.setTransferFee(null);
                    install.setTransferfeeDesc("")
                    // 总分期数 total_month
                    install.setTotalMonth(getFen(description).get(1));
                    // 当前分期数
                    install.setCurrentMonth(getFen(description).get(0));
                    // 币种
                    install.setCurrencyType("CNY");
                    // 本期金额
                    install.setAmountMoney(amount_money);

                    if((item + 1) != buttonCurrentPage.size()){
                        // 分期
                        if ((item + 1) <= buttonCurrentPage.size()) {// 下一条是分期手续费
                            var installmentList = getList(buttonCurrentPage, (item + 1));
                            if (installmentList.size() == 8) {
                                // 分期手续费
                                var handingFeeDesc = installmentList.get(2).trim().replaceAll("\\s*", "");
                                logger.info("{}页,{},这是下一条,手续费描述:{}", currentPage, context.get(LUA_TASK_KEY), handingFeeDesc);
                                var handingFee = installmentList.get(3).trim().replaceAll(",", "").replaceAll("￥", "");
                                if(handingFeeDesc.matches(".*[0-9]{2,}-[0-9]{2,}")){
                                    logger.info("{}页,-- {},分期里面的数字:handingFeeDesc[0]=:{}/handingFeeDesc[1]=:{}/description[0]=:{}/description[1]=:{}",currentPage,context.get(LUA_TASK_KEY),getFen(handingFeeDesc).get(0),getFen(handingFeeDesc).get(1),getFen(description).get(0),getFen(description).get(1));
                                    if (getFen(handingFeeDesc).get(0) == getFen(description).get(0)) {
                                        install.setHandingFee(!equals("",handingFee) ? new BigDecimal(handingFee) : new BigDecimal(0));
                                        install.setHandingfeeDesc(handingFeeDesc);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        logger.info("任务【{}】，已出账单明细第{}页，解析完成",context.get(LUA_TASK_KEY),currentPage);
    }catch (e){
        logger.error("taskKey={} 已出账单明细第{}页，解析异常{}", context.get(LUA_TASK_KEY),currentPage, e);
        statCaughtExceptionByJS(context, e);
    } finally {
        if (installments.size() != 0) {
            billAllData.setInstallments(installments);
        }
    }
}

/**
 * 处理已出账单的最终结果
 */
function handleFinalBillResult (oneMonth,creditCardAllData,yearStr,billAllData,monStr){
    // 账单月份 yyyy-MM
    billAllData.setBillMonth(oneMonth);
    // 账单日yyyy-MM-dd
    billAllData.setBillDate(yyyy_MM_dd_PARSER.parse(oneMonth + "-" + billDate));
    billAllData.setCreditLimit(creditCardAllData.getCreditLimit());
    billAllData.setCashLimit(creditCardAllData.getCashLimit());
    billAllData.setUsdNewBalance(null);
    billAllData.setUsdMinPayment(null);
    billAllData.setUsdLastBalance(null);
    billAllData.setUsdLastPayment(null);
    billAllData.setUsdNewCharges(null);
    billAllData.setUsdInterest(null);
    billAllData.setUsdCreditLimit(null);
    billAllData.setUsdCashLimit(null);

    //账单月和还款月是否相等
    if ("" !== billMonth && "" !== payMonth && payMonth.matches("[0-9]*")) {
        if (equals(billMonth, payMonth)) {
            billAllData.setPaymentDueDate(yyyy_MM_dd_PARSER.parse(yearStr + "-" + billMonth + "-" + repaymentDate));
        } else {
            var monStrBill = "";
            var monBill = monStr + 1;
            monBill =  Integer.parseInt(monBill);
            logger.info("{},已出账单的还款月:{}",context.get(LUA_TASK_KEY),monBill);
            if (monBill < 10) {
                monStrBill = "0" + monBill;
            } else if (monStr == 12) {
                monStrBill = "01";
                yearStr = Integer.parseInt(yearStr) + 1;
            } else {
                monStrBill = "" + monBill;
            }
            yearStr = Integer.parseInt(yearStr);
            logger.info("{},已出账单的年:{}",context.get(LUA_TASK_KEY),yearStr);
            // 还款日yyyy-MM-dd
            billAllData.setPaymentDueDate(yyyy_MM_dd_PARSER.parse(yearStr + "-" + monStrBill + "-" + repaymentDate));

        }
    }else {//
        var monStrBill = "";
        var monBill = monStr + 1;
        monBill =  Integer.parseInt(monBill);
        logger.info("{},没有已出账单记录的,已出账单的还款月:{}",context.get(LUA_TASK_KEY),monBill);
        if (monBill < 10) {
            monStrBill = "0" + monBill;
        } else if (monStr == 12) {
            monStrBill = "01";
            yearStr = Integer.parseInt(yearStr) + 1;
        } else {
            monStrBill = "" + monBill;
        }
        yearStr = Integer.parseInt(yearStr);
        logger.info("{},没有已出账单记录的,已出账单的年:{}",context.get(LUA_TASK_KEY),yearStr);
        if(equals("",repaymentDate)){
            // 还款日yyyy-MM-dd
            billAllData.setPaymentDueDate(yyyy_MM_dd_PARSER.parse(yearStr + "-" + monStrBill + "-05"));
        }else {
            // 还款日yyyy-MM-dd
            billAllData.setPaymentDueDate(yyyy_MM_dd_PARSER.parse(yearStr + "-" + monStrBill + "-" + repaymentDate));
        }
    }
   // bills.add(billAllData);
}

/**
 * 判断字符串里面是否包含数字
 * @param description
 * @return
 */
function isNumExists(description) {
    return description.matches(".*\\d+.*");
}

/**
 * 获取事件 描述的
 * @param buttons
 * @param item
 * @return
 */
function getList(buttons, item) {
    var data = buttons.get(item).attr("onclick");
    data = getGroupByIndex("javascript:QueryListDetail\\((.*)\\);", data, 1);
    data = data.replace(/\s*/g,"")
    //'01-05', '01-06','支付宝 -The Dairy Farm Company, Limited','￥1,190.61','7627','CN','01-05','￥1,190.61'
    var split = data.split("\',\'");
    var newResult = "[";
    //处理掉因为含有单引号引起的json解析异常
    for(var i = 0;i < split.length;i++){
        split[i] = "\'" + split[i].replaceAll("\\'","") + "\'";
        if(i<split.length-1){
            newResult = newResult + split[i] + ",";
        }else{
            newResult = newResult+split[i]+"]";
        }
    }
    //print(newResult);
    var list = JSON.parseObject(newResult, List.class);
    return list;
}

/**
 * 把分期描述里的数字提取出来
 * @param des
 * @return
 */
function getFen(des) {
    return findAll("\\d+",des,0);
}

/**
 * 12个月
 * @return
 */
/*function getRecentMonth() {
    var dates = new ArrayList();
    var sdf = new SimpleDateFormat("YYYY-MM");
    var cld = Calendar.getInstance();
    for (var i = 0; i < 12; i++) {
        var day = cld.get(Calendar.DAY_OF_MONTH);

        if (day <= Integer.parseInt(billDate)) {
            cld.add(Calendar.MONTH, -1);
            dates.add(sdf.format(cld.getTime()));
        } else {
            dates.add(sdf.format(cld.getTime()));
            cld.add(Calendar.MONTH, -1);
        }
    }
    return dates;
}*/

/**
 * 卡号后四位
 * @param cardNumList
 * @return
 */
function getCardNumList(cardNumList) {
    var sb = new StringBuffer();
    for (var i = 0; i < cardNumList.size(); i++) {
        sb.append(cardNumList.get(i).substring((cardNumList.get(i).length() - 4), (cardNumList.get(i).length()))).append(",");
    }
    return sb.toString().trim().substring(0, sb.toString().length - 1);
}

/**
 * 卡号集合
 * @param list
 * @return
 */
function list2String(list) {
    var sb = new StringBuffer();
    for (var i = 0; i < list.size(); i++) {
        sb.append(list.get(i)).append(",");
    }
    return sb.toString().trim().substring(0, sb.toString().length - 1);
}

function accountAndcardNoAndbilloutAndbillHeaders() {
    var headers = new HashMap();
    headers.put("User-Agent", params.USER_AGENT);
    headers.put("Accept", params.ACCEPT);
    headers.put("Host", "html.m.cmbchina.com");
    headers.put("Origin", "https://html.m.cmbchina.com");
    headers.put("Referer", "https://html.m.cmbchina.com/MobileHtml/Login/MsgVerify.aspx");
    headers.put("Accept-Encoding", "gzip, deflate, br");
    headers.put("Connection", "keep-alive");
    headers.put("Content-Type", "application/x-www-form-urlencoded");
    return headers;
}

/**
 * 获取登录页面
 * @returns {*}
 */
function getLoginHtml() {
    var xmlReq = context.get("xmlReq");
    var parametersMap = new HashMap();
    parametersMap.put("ClientNo", clientNo);
    parametersMap.put("Command", "CMD_DOLOGIN");
    parametersMap.put("XmlReq", xmlReq);

    var header = new HashMap();
    header.put("User-Agent", params.USER_AGENT);
    header.put("Accept", params.ACCEPT);
    header.put("Host", "html.m.cmbchina.com");
    header.put("Origin", "https://html.m.cmbchina.com");
    header.put("Referer", "https://html.m.cmbchina.com/MobileHtml/Login/LoginC.aspx");

    //var loginHtml = post(urls.LOGIN_C_URL, parametersMap, header);
    var loginHtml = cmbSubmitWithReTry(urls.LOGIN_C_URL,parametersMap,header,"登录","post")
    storeDataToFile(context, loginHtml, "loginSucc1.html");
    return loginHtml;
}

/**
 * 判断是否登录成功
 *
 * @param html
 * @return
 */
function loginSuccess(html) {
    return StringUtils.contains(html, SUCCESS_TAG);
}

/**
 * 判断是否需要短信验证码
 *
 * @param html
 * @return
 */
function needSms(html) {
    return StringUtils.contains(html, SMS_TAG);
}

// 取得登录错误消息
function getLoginErrMsg(html) {
    return getGroupByIndex("errMsg = \"(.*)\";", html, 1);
}

/**
 * 没有errMsg的内容
 *
 * @param html
 * @return
 */
function getAlertMsg(html) {
    return getGroupByIndex("alert\\(\"(.*)\"\\);", html, 1);
}

// 取得短信验证码消息
function getSmsErrMsg(html) {
    return Jsoup.parse(html).select("#TitleMsg").text();
}

//带有重试的方法，在catch到ssl异常的时候进行重试,重试次数，url,参数，头，方法描述
function cmbSubmitWithReTry(url,params,header,desc,type){
    var html = "";
    var tryTimes = 0;
    var totalCounts = 3;
    do{
        tryTimes++;
        logger.info("taskkey={},"+desc+"抓取正在进行第"+tryTimes+"次尝试..",context.get(LUA_TASK_KEY));
        try{
            //休眠半秒
            Thread.sleep(500);
            if(type.equals("post")){
                html = post(url, params, header);
            }else{
                html = get(url);
            }
            if(tryTimes>5){
                break;
            }
            //如果提示操作频繁，休眠十秒然后继续操作
            if(html.contains("请不要频繁操作，稍候片刻再返回菜单继续使用")){
                logger.info("taskkey={},网站返回操作频繁，等待进行重试..",context.get(LUA_TASK_KEY))
                Thread.sleep(5000);
                totalCounts++;
                continue;
            }
            if(StringUtils.isNotBlank(html) && !html.contains("请不要频繁操作，稍候片刻再返回菜单继续使用")){
                logger.info("taskKey={},"+desc+"抓取第"+tryTimes+"次尝试成功..",context.get(LUA_TASK_KEY));
                break;
            }
        }catch (e){
            // html.m.cmbchina.com:443 failed to respond
            if(e.message.contains("443 failed to respond")){
                logger.error("taskKey={} " + desc + "抓取异常443：{}", context.get(LUA_TASK_KEY), e);
                continue;
            }
            if(!(e.message.contains("Remote host closed connection during handshake") || e.message.contains("SSL peer shut down incorrectly")) || tryTimes>=3){
                logger.error("taskKey={} " + desc + "抓取异常：{}", context.get(LUA_TASK_KEY), e);
                statCaughtExceptionByJS(context, e);
                break;
            }
            logger.info("taskKey={},"+desc+"抓取请求发生ssl异常，准备重试..",context.get(LUA_TASK_KEY));
        }
    }while(tryTimes < totalCounts);
    return html;
}
function getTheTransDate(description,recordDate){
    if(description.split("-").length >=1){
        var installment_str = description.split("-")[0];
        print(installment_str)
        var installment_num = Number(getNum(installment_str));
        print(installment_num)
        var calendar2 = Calendar.getInstance();
        var the_year = calendar2.get(Calendar.YEAR);
        return AddMouth(recordDate,installment_num-1,"sub");
    }else{
        logger.info("日期获取出错")
        return recordDate
    }
}

function getNum(text){
    var value = text.replace(/[^0-9]/ig,"");
    return value
}

/**
 * 增加获取减去某个月份，增加暂时不完善
 * @param date
 * @param num
 * @returns {string}
 * @constructor
 */
function AddMouth(date, num,type)
{
    if(type == "add"){
        var mouthnum = parseInt(num);
        var year = parseInt(date.substring(0, 4));
        var mouth = parseInt(date.substring(5, 7));
        var day = parseInt(date.substring(8, 10));
        if (mouth + mouthnum > 12)
        {
            var newyear = year + 1;
            var newmouth = mouth + mouthnum - 12;
            var newday = day;
        }
        else
        {
            var newyear = year
            var newmouth = mouth + mouthnum;
            var newday = day;
        }
        var newdate = newyear + "-" + newmouth + "-" + newday;
        return newdate;
    }else{
        var mouthnum = parseInt(num);
        var year = parseInt(date.substring(0, 4));
        var mouth = parseInt(date.substring(5, 7));
        var day = parseInt(date.substring(8, 10));
        if (mouth - mouthnum < 0)
        {
            var num1 = Math.floor((mouthnum-mouth)/12)+1
            if(num1<=1){
                var newyear = year - 1;
                var newmouth = 0
                if(mouth + mouthnum - 12 > 0){
                    newmouth = (mouth - mouthnum) + 12;
                }else{
                    newmouth = 12 - (mouthnum-mouth);
                }
            }else{
                mouthnum = mouthnum % 12
                var newyear = year - num1;
                if(mouth - mouthnum <= 0){
                    var newmouth = 0
                    if(mouth + mouthnum - 12 > 0){
                        newmouth = (mouth - mouthnum) + 12;
                    }else{
                        newmouth = 12 - (mouthnum-mouth);
                    }
                }else{
                    newmouth = mouth-mouthnum
                }
            }
            var newday = day;
        }
        else
        {
            var newyear = year
            var newmouth = mouth - mouthnum;
            var newday = day;
        }
        var newdate = newyear + "-" + newmouth + "-" + newday;
        return newdate;
    }
}
